package com.alex.statistics.global;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;

/**
 * Controller方法调用日志AOP，记录调用信息及运行时间
 */
@Aspect
@Component
public class ControllerLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(ControllerLogAspect.class);
    
    // 记录方法开始时间
    private ThreadLocal<Long> startTime = new ThreadLocal<>();

    /**
     * 定义切入点：拦截所有标注了@Controller或@RestController的类中的方法
     */
    @Pointcut("@within(org.springframework.stereotype.Controller) || @within(org.springframework.web.bind.annotation.RestController)")
    public void controllerPointcut() {}

    /**
     * 前置通知：在Controller方法执行前记录请求信息
     */
    @Before("controllerPointcut()")
    public void doBefore(JoinPoint joinPoint) {
        // 记录开始时间
        startTime.set(System.currentTimeMillis());
        
        // 获取请求信息
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            
            // 打印请求基本信息
            logger.info("========================================== 开始请求 ==========================================");
            logger.info("请求URL: {}", request.getRequestURL().toString());
            logger.info("请求方法: {}", request.getMethod());
            logger.info("请求IP: {}", request.getRemoteAddr());
            logger.info("调用方法: {}.{}", 
                    joinPoint.getSignature().getDeclaringTypeName(), 
                    joinPoint.getSignature().getName());
        }
    }

    /**
     * 环绕通知：计算方法执行时间
     */
    @Around("controllerPointcut()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 执行目标方法
        Object result = proceedingJoinPoint.proceed();
        return result;
    }

    /**
     * 后置通知：在Controller方法执行后记录返回信息和执行时间
     */
    @AfterReturning(returning = "result", pointcut = "controllerPointcut()")
    public void doAfterReturning(Object result) {
        // 计算执行时间
        long endTime = System.currentTimeMillis();
        long time = endTime - startTime.get();
        startTime.remove(); // 清除ThreadLocal
        
        // 打印返回信息和执行时间
        logger.info("方法执行时间: {} ms", time);
        logger.info("========================================== 结束请求 ==========================================\n");
    }

    /**
     * 异常通知：记录方法执行异常
     */
    @AfterThrowing(pointcut = "controllerPointcut()", throwing = "e")
    public void doAfterThrowing(Throwable e) {
        long endTime = System.currentTimeMillis();
        long time = endTime - startTime.get();
        startTime.remove();
        
        logger.error("方法执行异常！执行时间: {} ms", time, e);
        logger.info("========================================== 请求异常结束 ==========================================\n");
    }
}
